import gate.util.GateException;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import org.w3c.dom.Document;

import com.hp.hpl.jena.ontology.Individual;
import com.hp.hpl.jena.ontology.OntClass;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.util.iterator.ExtendedIterator;

import ru.edu.niimm.mapping.linguistic.NLP;
import ru.ksu.ktk.diploma.sdi.analyzer.FieldPair;
import ru.ksu.ktk.diploma.sdi.dao.ResourcesDao;
import ru.ksu.ktk.diploma.sdi.mapping.loaders.ThesauriLoader;
import ru.ksu.ktk.diploma.sdi.mapping.resolver.MappingElementsResolver;
import ru.ksu.ktk.diploma.sdi.mapping.thesauri.ThesauriDescriptor;
import ru.ksu.ktk.diploma.sdi.schema.SchemaGenerator;

@ContextConfiguration(locations = {"classpath:META-INF/spring/mapping-beans.xml", "classpath:test-dao-beans.xml",
    "classpath:test-properties-beans.xml", "classpath:META-INF/spring/analyzer-beans.xml"})
public class InstanceAnalyzerTestCase extends AbstractTestNGSpringContextTests {
    private static final String BASE_NAMESPACE = "http://aat.cir.ru#";
    private static final String NLP_KEY = "instanceAnalyzerTest";

    private static final String sourceName = "emku";

    private static final String OUTPUT_FILE_NAME = String.format("C://temp//%s//out//%s.txt", sourceName, "pairs");

    @Autowired
    ResourcesDao resourcesDao;
    @Autowired
    SchemaGenerator schemaGenerator;
    @Autowired
    NLP nlpService;
    @Autowired
    ThesauriLoader thesauriLoader;

    OntModel thesauri;

    @Test(groups = "instance")
    public void testInstanceAnalyzer() throws IOException {
        String uri = "xmldb:exist://localhost:8080/exist/xmlrpc/db/mom/data/emku/sources/material-and-technique.xml";
        String contentSource = resourcesDao.loadContent(uri);

        InputStream stream = new ByteArrayInputStream(contentSource.getBytes("UTF-8"));
        OntModel sourceOntModel = schemaGenerator.generateSchema(stream);
        ExtendedIterator individualsIterator = sourceOntModel.listIndividuals();
        while (individualsIterator.hasNext()){
            Individual individual = (Individual) individualsIterator.next();
            individual.getRDFType();
        }
    }

    @Test(groups = "doubleRow")
    public void testDoubleFieldRowAnalyzer() throws GateException, IOException {
        thesauri = thesauriLoader.load();
        String fileName = "emku-name-technique.xml";
        String outputFilePath = String.format("C://temp//%s//out//%s.txt", sourceName, "pairs-name-technique");
        analyzerDoubleFieldRow(fileName, outputFilePath);
    }

    private void analyzerDoubleFieldRow(String fileName, String outputFileName) throws GateException, IOException {
        String uri =
            String.format("http://localhost:8080/exist/rest/db/mom/data/%s/sources/%s", sourceName, fileName);
        Document document = resourcesDao.getDOM(uri);
        List<DoubleFieldRowUtil.DoubleFieldItem> items = DoubleFieldRowUtil.getItems(document);

        List<FieldPair> pairs = new ArrayList<FieldPair>();
        for (DoubleFieldRowUtil.DoubleFieldItem item : items){
            List<String> firstFieldList = parseDescriptors(item.getFirstFieldContent());
            List<String> secondFieldList = parseDescriptors(item.getSecondFieldContent());
            FieldPair pair = new FieldPair(item.getId());
            pair.setFirstFieldList(firstFieldList);
            pair.setSecondFieldList(secondFieldList);
            pairs.add(pair);
        }
        write(pairs, outputFileName);
    }

    @Test(groups = "vrmRow")
    public void testSingleFieldRowAnalyzer() throws GateException, IOException {
        thesauri = thesauriLoader.load();
        String uri =
            String.format("http://localhost:8080/exist/rest/db/mom/data/vrm/sources/material-and-technique.xml",
                sourceName);
        Document document = resourcesDao.getDOM(uri);
        List<SingleFieldRowUtil.Item> items = SingleFieldRowUtil.getItems(document);
        List<FieldPair> pairs = new ArrayList<FieldPair>();
        for (SingleFieldRowUtil.Item item : items){
            ParseData parseData = parseDescriptorsData(item.getText());
            if (parseData.getTechniqueDescBlocks().isEmpty()) continue;
            FieldPair pair = new FieldPair(item.getId());
            pair.setFirstFieldList(parseData.getMaterialDescBlocks());
            pair.setSecondFieldList(parseData.getTechniqueDescBlocks());
            pairs.add(pair);
        }
        write(pairs, OUTPUT_FILE_NAME);
    }

    @Test(groups = "mikuRow")
    public void testMikuRowAnalyzer() throws GateException, IOException {
        thesauri = thesauriLoader.load();
        String uri =
            String.format("http://localhost:8080/exist/rest/db/mom/data/miku/sources/technique-and-material.xml",
                sourceName);
        Document document = resourcesDao.getDOM(uri);
        List<DoubleFieldRowUtil.DoubleFieldItem> items = DoubleFieldRowUtil.getItems(document);

        List<FieldPair> pairs = new ArrayList<FieldPair>();
        for (DoubleFieldRowUtil.DoubleFieldItem item : items){
            ParseData parseData = parseDescriptorsData(item.getFirstFieldContent());
            if (parseData.getTechniqueDescBlocks().isEmpty()) continue;
            List<String> secondFieldList = parseDescriptors(item.getSecondFieldContent());
            if (secondFieldList.isEmpty()) continue;
            FieldPair pair = new FieldPair(item.getId());
            pair.setFirstFieldList(parseData.getTechniqueDescBlocks());
            pair.setSecondFieldList(secondFieldList);
            pairs.add(pair);
        }
        write(pairs, OUTPUT_FILE_NAME);
    }

    private void write(List<FieldPair> pairs, String filePath) throws IOException {
        FileWriter fw = new FileWriter(filePath);

        for (FieldPair pair : pairs){
            fw.write(pair.toString());
            fw.write("\n");
        }
        fw.flush();
        fw.close();
    }

    private List<String> parseDescriptors(String content) throws GateException, IOException {
        List<String> descriptors = new ArrayList<String>();
        if (content != null && content.length() > 0){
            List<String> descNameList =
                nlpService.execOnFieldIndex(NLP_KEY, thesauriLoader.getThesauriUri(), new String[] {content}, "ru");

            for (String descName : descNameList){
                OntClass descBlock = getDescriptorBlock(descName);
                descriptors.add(descBlock.getURI());
            }
        }
        return descriptors;
    }

    private OntClass getDescriptorBlock(String descriptorName) {
        Individual individual = thesauri.getIndividual(BASE_NAMESPACE + descriptorName);
        OntClass descriptorBlock = thesauri.getOntClass(individual.getRDFType(true).getURI());
        return descriptorBlock;
    }

    private ParseData parseDescriptorsData(String content) throws GateException, IOException {
        ParseData parseData = new ParseData();

        if (content != null && content.length() > 0){
            List<String> descNameList =
                nlpService.execOnFieldIndex(NLP_KEY, thesauriLoader.getThesauriUri(), new String[] {content}, "ru");

            for (String descName : descNameList){
                OntClass descBlock = getDescriptorBlock(descName);
                if (isTechniqueBlock(descBlock)){
                    parseData.addTechniqueDescBlock(descBlock.getURI());
                }
                else{
                    parseData.addMaterialDescBlock(descBlock.getURI());
                }

            }
        }
        return parseData;

    }

    private boolean isTechniqueBlock(OntClass descBlock) {
        ExtendedIterator superClassesIterator = descBlock.listSuperClasses();
        while (superClassesIterator.hasNext()){
            OntClass superClass = (OntClass) superClassesIterator.next();
            if (superClass.getURI().endsWith("aatnum_creationTechniques_DB")) return true;
        }
        return false;
    }

    private static class ParseData {
        private List<String> materialDescBlocks = new ArrayList<String>();
        private List<String> techniqueDescBlocks = new ArrayList<String>();

        public List<String> getMaterialDescBlocks() {
            return materialDescBlocks;
        }

        public void setMaterialDescBlocks(List<String> materialDescBlocks) {
            this.materialDescBlocks = materialDescBlocks;
        }

        public List<String> getTechniqueDescBlocks() {
            return techniqueDescBlocks;
        }

        public void setTechniqueDescBlocks(List<String> techniqueDescBlocks) {
            this.techniqueDescBlocks = techniqueDescBlocks;
        }

        public void addMaterialDescBlock(String descBlock) {
            this.materialDescBlocks.add(descBlock);
        }

        public void addTechniqueDescBlock(String descBlock) {
            this.techniqueDescBlocks.add(descBlock);
        }
    }
}
